package com.mon4cc.simulation;

import com.mon4cc.cmon.adaption.BPMNAdaptor;
import com.mon4cc.simulation.utils.BpmnUtils;
import org.jbpt.pm.FlowNode;
import org.jbpt.pm.bpmn.BpmnControlFlow;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * todo: override do execute method based gateway semantics, and also the enabled status should need to consider token passing and consuming,
 * because of AndGateway
 * @author Ping
 * @date 2022/12/19 11:34
 **/
public class XorGatewaySimulatedInstance extends FlowNodeSimulatedInstance {
    public XorGatewaySimulatedInstance(TokenBasedBPMNSimulation bpmnSimulation, FlowNode flowNode) {
        super(bpmnSimulation, flowNode);
    }


    /**
     * For normal task flow node, the process to get next instance,
     * @param context
     * @return
     */
    @Override
    public FlowNodeSimulatedInstance nextInstance(ExecuteContext context) {
       FlowNode next = selectOneSatisfiedNextFlowNode(context) ;
        if (ObjectUtils.isEmpty(next)) {
            return null ;
        }
        if (BPMNAdaptor.isEndEvent(next)) {
            return BpmnUtils.toInstance(this.bpmnSimulation.getEndEvent(), this.bpmnSimulation) ;
        }
        context.updateEnabledSimulatedInstance(this,next);
        return context.getNextEnabledInstance();
    }
    /**
     * check each its conditional control flows, and choose one of its out control flow with its satisfied condition.
     * Meanwhile, invalidated-condition-type was need to considered.
     * @param context
     * @return
     */
    private FlowNode selectOneSatisfiedNextFlowNode(ExecuteContext context) {
        Set<BpmnControlFlow<FlowNode>> outputControlFlows = this.bpmnSimulation.getOutputControlflow(this.flowNode) ;
        Assert.notNull(outputControlFlows, "outputcontrol-flow should not be null.") ;
        FlowNode target = null ;
        List<BpmnControlFlow<FlowNode>> outputFlows = new ArrayList<>(outputControlFlows) ;
        Collections.shuffle(outputFlows);
        for (BpmnControlFlow<FlowNode> bcf : outputFlows) {
            // for conditional control flow
            if (bcf.isConditional()) {
                if (bcf.getConditional().isSatisfied(context.attrContext())) {
                    // if control flow is satisfied and the target flow node was not invovled in invalidated data assignemnt,
                    // then the target was selected.
                    if (! this.bpmnSimulation.containedInInvalidatedDataAssignementEvents(bcf.getTarget().getName())) {
                       target = bcf.getTarget();
                       break ;
                    }
                    // consider next control flow
                } else {
                    //if the condition was not satisfied and the target was involved in invalidated-type, then the
                    // target was selected.
                    if (this.bpmnSimulation.containedInInvalidatedDataAssignementEvents(bcf.getTarget().getName())) {
                        // error was injected successfully, and update its number.
                        context.increaseErrorNum();
                        target = bcf.getTarget();
                        break ;
                    }
                }
            } else {
                // for normal ouptput control flow, one of control-flows was selected randomly.
                target = bcf.getTarget();
                break ;
            }
        }
        Assert.notNull(target, "The target of output-flow should not null");
        return target ;
    }
}
